Skip to content

fix(@angular/build): rebuild stylesheets when files are added to a PostCSS directory dependency - #34451

Closed
MeAkib wants to merge 1 commit into
angular:mainfrom
MeAkib:fix/postcss-dir-dependency-watch
Closed

MeAkib wants to merge 1 commit into
angular:mainfrom
MeAkib:fix/postcss-dir-dependency-watch

Conversation

@MeAkib

@MeAkib MeAkib commented Oct 20, 2026

Copy link
Copy Markdown
Contributor

Previously, a PostCSS dir-dependency message (used by Tailwind CSS v4 for its content sources) was expanded with a glob and only the files that existed at build time were watched. Files created later, or moved by renaming their folder, did not invalidate the cached stylesheet, so classes used only in them were not generated until the stylesheet itself changed.

The directory is now also added to the watch files, and the load result cache invalidates an entry when a changed path is inside one of its watched directories.

Fixes #34446

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces support for directory dependencies (such as PostCSS dir-dependency) by watching the dependency directory itself and updating the cache invalidation logic to check parent directories recursively. The review feedback suggests normalizing the input path in the invalidate method to ensure OS-consistent path matching when looking up file dependencies.

@MeAkib
MeAkib force-pushed the fix/postcss-dir-dependency-watch branch from 22295e9 to 8462bbf Compare October 20, 2026 18:34
@alan-agius4 alan-agius4 added the action: review The PR is still awaiting reviews from at least one requested reviewer label Oct 21, 2026
@alan-agius4
alan-agius4 requested a review from clydin October 21, 2026 06:52
@alan-agius4 alan-agius4 added the target: patch This PR is targeted for the next patch release label Oct 21, 2026
…stCSS directory dependency

Previously, a PostCSS `dir-dependency` message (used by Tailwind CSS v4 for its content sources) was
expanded with a glob and only the files that existed at build time were watched. Files created later,
or moved by renaming their folder, did not invalidate the cached stylesheet, so classes used only in
them were not generated until the stylesheet itself changed.

The directory is now also added to the watch files, and the load result cache invalidates an entry
when a changed path is inside one of its watched directories.

Fixes angular#34446
@alan-agius4
alan-agius4 force-pushed the fix/postcss-dir-dependency-watch branch from 8462bbf to 3a72951 Compare October 30, 2026 16:05
@alan-agius4 alan-agius4 removed the action: review The PR is still awaiting reviews from at least one requested reviewer label Oct 30, 2026
@alan-agius4
alan-agius4 removed the request for review from clydin October 30, 2026 16:29

@alan-agius4 alan-agius4 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please see the inline comments.

) {
loadResult.watchFiles ??= [];
// Watch the directory itself so files added or moved into it later also cause a rebuild
loadResult.watchFiles.push(resultMessage['dir']);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding resultMessage['dir'] directly to watchFiles causes a few issues:

  1. watchFiles is intended for file paths rather than directories (for example PersistentLoadResultCache calls readFile on each entry in watchFiles, which fails with EISDIR).
  2. It ignores resultMessage['glob'] during invalidation and watching. When dir is src or the workspace root (such as in Tailwind v4), modifying any file under dir will trigger the watcher and invalidate the stylesheet cache, even for files that do not match glob.
  3. Because glob() is still executed below, all existing files and the parent directory are both added to watchFiles.

Comment on lines +111 to 132
let invalid = false;

// Check parent directories to support directory dependencies (PostCSS `dir-dependency`)
let current = normalize(path);
while (true) {
const affectedPaths = this.#fileDependencies.get(current);
if (affectedPaths) {
for (const affected of affectedPaths) {
this.#loadResults.delete(affected);
}
invalid = true;
}

for (const affected of affectedPaths) {
this.#loadResults.delete(affected);
const parent = dirname(current);
if (parent === current) {
break;
}
current = parent;
}

return true;
return invalid;
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Walking up dirname() to the filesystem root on every invalidate() call turns an O(1) lookup into an O(depth) walk for every changed file across all caches (including SourceFileCache and ComponentStylesheetBundler).

Also, after recent changes on main, BundlerContext.invalidate() checks !this.watchFiles.isDisjointFrom(candidateFiles) rather than the return value of this.#loadCache.invalidate(file). When a new file is created inside a watched directory, candidateFiles only has the new file path while this.watchFiles only has the directory path, so this.#esbuildResult is not cleared and the bundle is not rebuilt.

Lastly, normalize() in put() preserves trailing slashes on directory paths while dirname() strips them, so a dir with a trailing slash will not match.


// Emits a class for each source file, similar to Tailwind CSS v4 content scanning
await harness.writeFile(
'test-plugin.js',

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use a unique plugin filename here instead of test-plugin.js. An earlier test in this suite also uses ./test-plugin.js in .postcssrc.json, and StylesheetPluginFactory caches PostCSS processors by config and require.cache, which can cause the wrong plugin to be reused.

@alan-agius4

Copy link
Copy Markdown
Collaborator

Closing as this require a completely different approachl, likely integrate actual directory watching via the esbuild result objects.

@MeAkib
MeAkib deleted the fix/postcss-dir-dependency-watch branch October 30, 2026 19:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: @angular/build target: patch This PR is targeted for the next patch release

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Tailwind CSS v4 classes in new or moved files are not picked up by ng serve until another rebuild

2 participants